package com.iiiu.payment.exception;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.dev33.satoken.exception.SaTokenException;
import com.iiiu.payment.common.IgnoreResponseAdvice;
import com.iiiu.payment.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 全局异常处理器
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 处理Sa-Token未登录异常
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(NotLoginException.class)
    public Result<Object> handleNotLoginException(NotLoginException e) {
        logger.warn("用户未登录异常: {}", e.getMessage());
        
        String message = "用户未登录或登录已过期";
        
        // 根据不同的未登录类型返回不同的提示信息
        String type = e.getType();
        if (NotLoginException.NOT_TOKEN.equals(type)) {
            message = "未提供登录凭证";
        } else if (NotLoginException.INVALID_TOKEN.equals(type)) {
            message = "登录凭证无效";
        } else if (NotLoginException.TOKEN_TIMEOUT.equals(type)) {
            message = "登录凭证已过期";
        } else if (NotLoginException.BE_REPLACED.equals(type)) {
            message = "账号在其他地方登录，当前登录已被顶下线";
        } else if (NotLoginException.KICK_OUT.equals(type)) {
            message = "账号已被强制下线";
        }
        
        return Result.error(401, message);
    }

    /**
     * 处理Sa-Token权限不足异常
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(NotPermissionException.class)
    public Result<Object> handleNotPermissionException(NotPermissionException e) {
        logger.warn("权限不足异常: {}", e.getMessage());
        
        return Result.error(403, "权限不足，无法访问该资源");
    }

    /**
     * 处理Sa-Token角色不足异常
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(NotRoleException.class)
    public Result<Object> handleNotRoleException(NotRoleException e) {
        logger.warn("角色权限不足异常: {}", e.getMessage());
        
        return Result.error(403, "角色权限不足，无法访问该资源");
    }




    /**
     * 处理Sa-Token其他异常
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(SaTokenException.class)
    public Result<Object> handleSaTokenException(SaTokenException e) {
        logger.error("Sa-Token异常: {}", e.getMessage(), e);
        
        return Result.error(500, "认证服务异常: " + e.getMessage());
    }

    /**
     * 处理参数校验异常（@RequestBody）
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        logger.warn("参数校验失败: {}", e.getMessage());
        
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        String errorMessage = fieldErrors.stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining(", "));
        
        return Result.error(400, "参数校验失败: " + errorMessage);
    }

    /**
     * 处理参数绑定异常（@ModelAttribute）
     */
    @ExceptionHandler(BindException.class)
    public Result<Object> handleBindException(BindException e) {
        logger.warn("参数绑定失败: {}", e.getMessage());
        
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        String errorMessage = fieldErrors.stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining(", "));
        
        return Result.error(400, "参数绑定失败: " + errorMessage);
    }

    /**
     * 处理约束违反异常（@Validated）
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result<Object> handleConstraintViolationException(ConstraintViolationException e) {
        logger.warn("约束违反: {}", e.getMessage());
        
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        String errorMessage = violations.stream()
                .map(violation -> violation.getPropertyPath() + ": " + violation.getMessage())
                .collect(Collectors.joining(", "));
        
        return Result.error(400, "参数校验失败: " + errorMessage);
    }

    /**
     * 处理参数类型不匹配异常
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<Object> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        logger.warn("参数类型不匹配: {}", e.getMessage());
        String errorMessage = String.format("参数 '%s' 的值 '%s' 类型不正确，期望类型: %s", 
                e.getName(), e.getValue(), e.getRequiredType().getSimpleName());
        return Result.error(400, errorMessage);
    }

    /**
     * 处理IllegalArgumentException
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public Result<Object> handleIllegalArgumentException(IllegalArgumentException e) {
        logger.warn("非法参数异常: {}", e.getMessage());
        return Result.error(400, e.getMessage());
    }

    /**
     * 处理NullPointerException
     */
    @ExceptionHandler(NullPointerException.class)
    public Result<Object> handleNullPointerException(NullPointerException e) {
        logger.error("空指针异常: {}", e.getMessage(), e);
        return Result.error(500, "空指针异常: " + e.getMessage());
    }

    /**
     * 处理RuntimeException
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(RuntimeException.class)
    public Result<Object> handleRuntimeException(RuntimeException e) {
        logger.error("运行时异常: {}", e.getMessage(), e);
        return Result.error(400, e.getMessage());
    }

    /**
     * 处理其他未捕获的异常
     */
    @IgnoreResponseAdvice
    @ExceptionHandler(Exception.class)
    public Result<Object> handleException(Exception e) {
        logger.error("未知异常: {}", e.getMessage(), e);
        return Result.error(500, "服务器内部错误: " + e.getMessage());
    }

}